package data

import (
	"github.com/dtm-labs/rockscache"
	"github.com/duke-git/lancet/convertor"
	"github.com/go-redis/redis/extra/redisotel/v8"
	"github.com/go-redis/redis/v8"
	clientv3 "go.etcd.io/etcd/client/v3"
	"go.opentelemetry.io/otel/attribute"
	"gorm.io/gorm"
	"kratosEtcdWatcher/internal/conf"
	"time"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/google/wire"
	"gorm.io/driver/mysql"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewMysql, NewRedis, NewRocksCache, NewLotteryRepo)

// Data .
type Data struct {
	mysql      *gorm.DB
	redis      *redis.Client
	etcdClient *clientv3.Client
	rocksCache *rockscache.Client
}

// NewData .
func NewData(c *conf.Data, logger log.Logger, redis *redis.Client, clientv3Cli *clientv3.Client, mysql *gorm.DB, rocksCli *rockscache.Client) (*Data, func(), error) {
	cleanup := func() {
		log.NewHelper(logger).Info("closing the data resources")
	}
	return &Data{
		mysql:      mysql,
		redis:      redis,
		etcdClient: clientv3Cli,
		rocksCache: rocksCli,
	}, cleanup, nil
}

func NewMysql(c *conf.Data, logger log.Logger) *gorm.DB {
	// recover
	defer func() {
		if err := recover(); err != nil {
			log.NewHelper(logger).Errorw("kind", "mysql", "error", err)
		}
	}()
	// mysql数据库连接
	db, err := gorm.Open(mysql.Open(c.Database.Source))
	if err != nil {
		panic(err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		panic(err)
	}

	// 连接池参数设置
	sqlDB.SetMaxIdleConns(int(c.Database.MinIdleConns))
	sqlDB.SetMaxOpenConns(int(c.Database.MaxOpenConns))
	sqlDB.SetConnMaxLifetime(time.Hour * time.Duration(c.Database.ConMaxLeftTime))

	return db
}

func NewRedis(c *conf.Data, logger log.Logger) *redis.Client {
	rdb := redis.NewClient(&redis.Options{
		Addr: c.Redis.Addr,
		//Password:     c.Redis.Password,
		DB:           int(c.Redis.Db),
		PoolSize:     int(c.Redis.PoolSize),     // 连接池数量
		MinIdleConns: int(c.Redis.MinIdleConns), //好比最小连接数
		MaxRetries:   int(c.Redis.MaxRetries),   // 命令执行失败时，最多重试多少次，默认为0即不重试
	})
	rdb.AddHook(redisotel.NewTracingHook(
		redisotel.WithAttributes(
			attribute.String("db.type", "redis"),
			attribute.String("db.ip", c.Redis.Addr),
			attribute.String("db.instance", c.Redis.Addr+"/"+convertor.ToString(c.Redis.Db)),
		),
	))
	log.NewHelper(logger).Infow("kind", "redis", "status", "enable")
	return rdb
}

func NewRocksCache(c *conf.Data, logger log.Logger, rdb *redis.Client) *rockscache.Client {
	var dc = rockscache.NewClient(rdb, rockscache.NewDefaultOptions())
	// Notice 详细的demo参考我另外一个项目：https://gitee.com/huoyingwhw/kratos-rockscache
	// 常用参数设置
	// 1、强一致性(默认关闭强一致性，如果开启的话会影响性能)
	dc.Options.StrongConsistency = false

	// 2、redis出现问题需要缓存降级时设置为true
	dc.Options.DisableCacheRead = false   // 关闭缓存读，默认false；如果打开，那么Fetch就不从缓存读取数据，而是直接调用fn获取数据
	dc.Options.DisableCacheDelete = false // 关闭缓存删除，默认false；如果打开，那么TagAsDeleted就什么操作都不做，直接返回

	// 3、其他设置
	// 标记删除的延迟时间，默认10秒，设置为3秒表示：被删除的key在3秒后才从redis中彻底清除
	dc.Options.Delay = time.Second * time.Duration(3)
	// 防穿透：若fn返回空字符串，空结果在缓存中的缓存时间，默认60秒
	dc.Options.EmptyExpire = time.Second * time.Duration(120)
	// 防雪崩,默认0.1,当前设置为0.1的话，如果设定为600的过期时间，那么过期时间会被设定为540s - 600s中间的一个随机数，避免数据出现同时到期
	dc.Options.RandomExpireAdjustment = 0.1 // 设置为默认就行

	// Notice rockscache中自带 "防击穿"～

	log.NewHelper(logger).Infow("kind", "rocksCache", "status", "enable")
	return dc
}
